using System;
using System.Collections.Generic;
using System.Data;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Data;
using ScrumTable.Common.Excel.OpenXmlSdk.Extensions;

namespace ScrumTable.DL.Data.Excel.Services.Excel
{
    /// <summary>
    /// Defines a class to map a schema to a excel sheet.
    /// </summary>
    internal class SheetValueDefinition
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private const string DefaultMarker = "*";

        private readonly IList<object> _values = new List<object>();
        private readonly Type _valueType = typeof(string);

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Returns true if the current workbook instance contains a sheet with
        /// the given name.
        /// </summary>
        internal object DefaultValue { get; private set; }

        /// <summary>
        /// Gets the 'to' property content.
        /// </summary>
        internal IList<object> Values
        {
            get { return _values; }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new SheetValueDefinition instance.
        /// </summary>
        /// <param name="sheetValueDefinition">Specifies the underlying row with the value definitions.</param>
        /// <param name="rowDataType">Specifies the data type of the row values.</param>
        internal SheetValueDefinition(DataRow sheetValueDefinition, Type rowDataType = null)
        {
            PreCondition.AssertNotNull(sheetValueDefinition, "sheetValueDefinition");

            if (rowDataType != null)
            {
                _valueType = rowDataType;
            }
            InitMetaValues(sheetValueDefinition);
        }

        /// <summary>
        /// Creates a new SheetValueDefinition instance.
        /// </summary>
        /// <param name="rowDataType">Specifies the data type of the row values.</param>
        internal SheetValueDefinition(Type rowDataType = null)
        {
            if (rowDataType != null)
            {
                _valueType = rowDataType;
            }
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Determines whether the given value ends with the default marker.
        /// </summary>
        /// <param name="value">Specifies the value to check.</param>
        internal static bool IsDefaultValue(string value)
        {
            return (!string.IsNullOrEmpty(value) && value.EndsWith(DefaultMarker));
        }

        /// <summary>
        /// Converts the given value into a default value.
        /// </summary>
        /// <param name="value">Specifies the value to convert.</param>
        internal static string ToDefaultValue(object value)
        {
            return ObjectUtil.ConvertToString(value) + DefaultMarker;
        }

        /// <summary>
        /// Gets the value from default value.
        /// </summary>
        /// <param name="value">Specifies the value to convert.</param>
        internal static string GetValueFromDefaultValue(string value)
        {
            return (value != null)
                       ? value.Substring(0, value.Length - DefaultMarker.Length).Trim()
                       : string.Empty;
        }

        private void InitMetaValues(DataRow sheetValueDefinition)
        {
            sheetValueDefinition.GetColumns<DataValue>().ForEachIndex(
                (idx, col) =>
                    {
                        string metaValue = (col.Value != null)
                            ? ObjectUtil.ConvertToString(col.Value)
                            : string.Empty;

                        if (idx != 0 && !String.IsNullOrEmpty(metaValue))
                        {
                            if (IsDefaultValue(metaValue))
                            {
                                object resultValue;

                                if (ObjectUtil.TryConvertFromString(_valueType, GetValueFromDefaultValue(metaValue), out resultValue))
                                {
                                    DefaultValue = resultValue;
                                    Values.Add(resultValue);
                                }
                            }
                            else
                            {
                                object resultValue;

                                if (col.Value.TryChangeType(_valueType, out resultValue))
                                {
                                    Values.Add(resultValue);
                                }
                            }
                        }
                    });
        }

        #endregion
    }
}